// Graph.cpp
// Graph class implementation

#include "Graph.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>

// Helper template function for safe string to optional_value conversion
template<typename T>
optional_value<T> safe_string_to_num(const std::string& s) {
    if (s.empty() || s.find_first_not_of(" \t\n\v\f\r") == std::string::npos) { 
        return optional_value<T>(); 
    }
    try {
        if (std::is_same<T, long long>::value) { 
            return optional_value<T>(std::stoll(s)); 
        }
        else if (std::is_same<T, int>::value) { 
            return optional_value<T>(std::stoi(s)); 
        }
    }
    catch (const std::exception&) { 
        return optional_value<T>(); 
    }
    return optional_value<T>();
}

// Enhanced CSV line parser that can handle quoted fields containing commas
std::vector<std::string> parse_csv_line(const std::string& line) {
    std::vector<std::string> fields;
    std::string current_field;
    bool in_quotes = false;
    for (char c : line) {
        if (c == '"') { 
            in_quotes = !in_quotes; 
        }
        else if (c == ',' && !in_quotes) { 
            fields.push_back(current_field); 
            current_field.clear(); 
        }
        else { 
            current_field += c; 
        }
    }
    fields.push_back(current_field);
    return fields;
}

bool Graph::load_from_csv(const std::string& nodes_csv_path, const std::string& edges_csv_path, CodeMap& code_map) {
    std::ifstream nodes_file(nodes_csv_path);
    if (!nodes_file.is_open()) {
        std::cerr << "Error: Cannot open CSV file: " << nodes_csv_path << std::endl;
        return false;
    }
    
    std::string line;
    std::getline(nodes_file, line);
    std::map<std::string, int> op_map, type_map;
    int op_counter = 1, type_counter = 1;
    std::vector<NodeData> raw_nodes_data;
    int line_num = 1;
    
    while (std::getline(nodes_file, line)) {
        line_num++;
        if (!line.empty() && line.back() == '\r') { 
            line.pop_back(); 
        }
        if (line.empty()) { 
            continue; 
        }
        
        std::vector<std::string> fields = parse_csv_line(line);
        if (fields.size() < 8) {
            std::cerr << "Error: Line " << line_num << " has incorrect format, field count (" << fields.size() << "), skipping." << std::endl;
            continue;
        }
        
        NodeData current_node;
        auto id_opt = safe_string_to_num<int>(fields[0]);
        if (!id_opt) {
            std::cerr << "Error: Line " << line_num << " has invalid ID ('" << fields[0] << "'), skipping." << std::endl;
            continue;
        }
        
        current_node.id = *id_opt + 1;
        current_node.op = fields[1];
        current_node.buf_id = safe_string_to_num<int>(fields[2]);
        current_node.size = safe_string_to_num<long long>(fields[3]);
        current_node.type = fields[4];
        current_node.pipe = fields[5];
        current_node.cycles = safe_string_to_num<long long>(fields[6]);
        current_node.bufs = safe_string_to_num<int>(fields[7]);
        
        raw_nodes_data.push_back(current_node);
        if (!current_node.op.empty() && op_map.find(current_node.op) == op_map.end()) { 
            op_map[current_node.op] = op_counter++; 
        }
        if (!current_node.type.empty() && type_map.find(current_node.type) == type_map.end()) { 
            type_map[current_node.type] = type_counter++; 
        }
    }
    nodes_file.close();

    if (op_map.count("ALLOC")) code_map.ALLOC = op_map["ALLOC"];
    if (op_map.count("FREE")) code_map.FREE = op_map["FREE"];
    if (type_map.count("L1")) code_map.L1 = type_map["L1"];
    if (type_map.count("UB")) code_map.UB = type_map["UB"];
    if (type_map.count("L0A")) code_map.L0A = type_map["L0A"];
    if (type_map.count("L0B")) code_map.L0B = type_map["L0B"];
    if (type_map.count("L0C")) code_map.L0C = type_map["L0C"];
    
    size_t max_id = 0;
    for (const auto& raw_node : raw_nodes_data) { 
        if (raw_node.id > max_id) { 
            max_id = raw_node.id; 
        } 
    }
    
    nodes_.resize(max_id);
    for (const auto& raw_node : raw_nodes_data) {
        int zero_based_id = raw_node.id - 1;
        nodes_[zero_based_id] = raw_node;
        if (!raw_node.op.empty()) {
            nodes_[zero_based_id].op_code = op_map[raw_node.op];
        }
        if (!raw_node.type.empty() && type_map.count(raw_node.type)) { 
            nodes_[zero_based_id].type_code = type_map.at(raw_node.type); 
        }
    }

    std::ifstream edges_file(edges_csv_path);
    if (!edges_file.is_open()) {
        std::cerr << "Error: Cannot open CSV file: " << edges_csv_path << std::endl;
        return false;
    }
    
    adj_list_.resize(get_num_nodes() + 1);
    std::getline(edges_file, line);
    while (std::getline(edges_file, line)) {
        std::stringstream ss(line);
        std::string field;
        int start_node, end_node;
        std::getline(ss, field, ','); 
        start_node = std::stoi(field);
        std::getline(ss, field, ','); 
        end_node = std::stoi(field);
        adj_list_[start_node + 1].push_back(end_node + 1);
    }
    edges_file.close();

    // Build predecessors list based on adjacency list
    predecessors_list_.assign(get_num_nodes() + 1, std::vector<int>());
    for (int u = 1; u <= get_num_nodes(); ++u) {
        for (int v : adj_list_[u]) {
            if (v >= 1 && v <= get_num_nodes()) {
                predecessors_list_[v].push_back(u);
            }
        }
    }

    return true;
}

int Graph::get_num_nodes() const { 
    return static_cast<int>(nodes_.size()); 
}

const NodeData& Graph::get_node_data(int node_id) const {
    if (node_id < 1 || node_id > nodes_.size()) { 
        throw std::out_of_range("Node ID " + std::to_string(node_id) + " is out of range."); 
    }
    return nodes_[node_id - 1];
}

const std::vector<int>& Graph::get_successors(int node_id) const {
    if (node_id < 1 || node_id >= adj_list_.size()) { 
        throw std::out_of_range("Node ID " + std::to_string(node_id) + " is out of range for successors list."); 
    }
    return adj_list_[node_id];
}

const std::vector<int>& Graph::get_predecessors(int node_id) const {
    if (node_id < 1 || node_id >= predecessors_list_.size()) { 
        throw std::out_of_range("Node ID " + std::to_string(node_id) + " is out of range for predecessors list."); 
    }
    return predecessors_list_[node_id];
}

std::vector<int> Graph::get_indegrees() const {
    std::vector<int> in_degrees(get_num_nodes() + 1, 0);
    for (size_t i = 1; i < adj_list_.size(); ++i) { 
        for (int successor : adj_list_[i]) { 
            if (successor < in_degrees.size()) { 
                in_degrees[successor]++; 
            } 
        } 
    }
    return in_degrees;
}